package com.clp.protocol.iec104.server.pipeline.state.data;

import com.clp.protocol.iec104.apdu.asdu.Cot;
import com.clp.protocol.iec104.apdu.asdu.IAsdu;
import com.clp.protocol.iec104.apdu.asdu.infoobj.CInfoObj;
import com.clp.protocol.iec104.apdu.asdu.infoobj.InfoObj;
import com.clp.protocol.iec104.definition.TypeTag;
import com.clp.protocol.iec104.definition.cot.Cause;
import com.clp.protocol.iec104.definition.cot.Pn;
import com.clp.protocol.iec104.server.SlaveDataConfig;
import com.clp.protocol.iec104.server.pipeline.PipelineManager;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelPromise;
import lombok.extern.slf4j.Slf4j;

import javax.annotation.Nullable;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiConsumer;

@Slf4j
public class TotalCall101DataStateHandler extends AbstractDataStateHandler {

    private enum State {
        /**
         * 新请求到达
         */
        R_NEW_REQ,
        /**
         * 总召唤肯定确认
         */
        S_ACK_YES,
        /**
         * 总召唤否定确认
         */
        S_ACK_NO,
        /**
         * 正在传输遥脉数据
         */
        S_TP_TRANSMITTING,
        /**
         * 总召唤终止
         */
        FINISHED;
    }

    private final TotalCall101DataReactor dataReactor;
    private volatile State state;

    public TotalCall101DataStateHandler(PipelineManager manager, SlaveDataConfig dataConfig) {
        super(manager);
        this.dataReactor = dataConfig.getTotalCall101DataReactor();
    }

    @Override
    protected void resetState() {
        state = State.FINISHED;
    }

    @Override
    protected void afterResetState() {

    }

    private void checkStateIn(State... states) {
        for (State stateToCheck : states) {
            if (state == stateToCheck) return;
        }
        throw new IllegalStateException("当前状态：" + state + "，需要状态：" + Arrays.toString(states));
    }

    private void setState(State stateToSet) {
        state = stateToSet;
    }

    @Override
    protected IAsdu updateStateByRecv(IAsdu iAsdu) throws Exception {
        TypeTag typeTag = iAsdu.getTypeTag();
        Cot cot = iAsdu.getCot();
        Cause cause = cot.getCause();
        if (typeTag == TypeTag.C_CI_NA_1) { // 计数量召唤
            if (cause == Cause.COT_ACT) { // 激活
                checkStateIn(State.FINISHED);
                setState(State.R_NEW_REQ);
                CompletableFuture<Boolean> acceptFuture = dataReactor.accept(inSlaveChannel());
                acceptFuture.whenComplete(new BiConsumer<Boolean, Throwable>() {
                    @Override
                    public void accept(Boolean isAccepted, Throwable throwable) {
                        if (throwable != null) {
                            throwable.printStackTrace();
                            return;
                        }
                        if (isAccepted) {
                            // 发送肯定确认
                            iAsduSender().chSendTotalCall101Ack(Pn.PN_YES).addListener(new ChannelFutureListener() {
                                @Override
                                public void operationComplete(ChannelFuture future) throws Exception {
                                    if (!future.isSuccess()) {
                                        future.cause().printStackTrace();
                                        return;
                                    }

                                    // 肯定确认之后需要发送遥脉报文
                                    sendTpData().addListener(new ChannelFutureListener() {
                                        @Override
                                        public void operationComplete(ChannelFuture future) throws Exception {
                                            if (!future.isSuccess()) {
                                                future.cause().printStackTrace();
                                                return;
                                            }

                                            log.info("[TotalCall101] 总召唤101-遥脉响应报文发送结束");

                                            // 发送遥脉报文之后发送激活终止
                                            iAsduSender().chSendTotalCall101Finished().addListener(new ChannelFutureListener() {
                                                @Override
                                                public void operationComplete(ChannelFuture future) throws Exception {
                                                    if (!future.isSuccess()) {
                                                        future.cause().printStackTrace();
                                                        return;
                                                    }

                                                    log.info("[TotalCall101] 总召唤101-已终止");
                                                }
                                            });
                                        }
                                    });
                                }
                            });
                        } else {
                            // 发送否定确认
                            iAsduSender().chSendTotalCall101Ack(Pn.PN_NO);
                        }
                    }
                });
            }
        }
        return iAsdu;
    }

    private ChannelFuture sendTpData() {
        ChannelPromise sendPromise = channel.newPromise();
        dataReactor.collectTpData(inSlaveChannel()).whenComplete(new BiConsumer<List<TpData>, Throwable>() {
            @Override
            public void accept(List<TpData> tpData, Throwable throwable) {
                if (throwable != null) {
                    throw new RuntimeException(throwable);
                }
                if (tpData == null) {
                    throw new NullPointerException();
                }
                // 如果数据为空，则直接发送成功
                if (tpData.isEmpty()) {
                    sendPromise.setSuccess();
                }

                List<List<InfoObj>> infoObjsList = new LinkedList<>(); // 待发送的asdu集合

                TotalCall101DataReactor.TpDataType tpDataType = dataReactor.getTpDataType();
                int maxCount = tpDataType.getMaxCount(); // 单个asdu可用最大数量
                // 构造apdu并发送
                // 进行排序
                tpData.sort(TpData.Comparator.INSTANCE);
                // 对于信息体地址缺失的，使用无效位代替
                int nextAddress = tpData.get(0).getAddress(); // 起始地址
                ListIterator<TpData> iter = tpData.listIterator();
                List<InfoObj> infoObjs = new ArrayList<>(maxCount);
                int currCount = 0;
                while (iter.hasNext()) {
                    CInfoObj cInfoObj = tpDataType.convert(iter.next());
                    int addr = cInfoObj.getAddr();

                    if (currCount == maxCount) {
                        // 达到最大值
                        infoObjsList.add(infoObjs);
                        infoObjs = new ArrayList<>(maxCount);
                        currCount = 0;
                    }

                    while (nextAddress < addr) {
                        if (currCount == maxCount) {
                            // 达到最大值
                            infoObjsList.add(infoObjs);
                            infoObjs = new ArrayList<>(maxCount);
                            currCount = 0;
                        }
                        CInfoObj invalidCInfoObj = tpDataType.convertInvalid(nextAddress);
                        infoObjs.add(invalidCInfoObj);
                        nextAddress++;
                        currCount++;
                    }

                    if (currCount == maxCount) {
                        // 达到最大值
                        infoObjsList.add(infoObjs);
                        infoObjs = new ArrayList<>(maxCount);
                        currCount = 0;
                    }

                    infoObjs.add(cInfoObj);
                    nextAddress++;
                    currCount++;
                }
                if (!infoObjs.isEmpty()) {
                    infoObjsList.add(infoObjs);
                }

                // 一个一个发送
                sendTpInfoObj(tpDataType, infoObjsList.listIterator(), sendPromise);
            }
        });

        return sendPromise;
    }

    private void sendTpInfoObj(TotalCall101DataReactor.TpDataType tpDataType, Iterator<List<InfoObj>> iter, ChannelPromise promise) {
        if (!iter.hasNext()) {
            promise.setSuccess(); // 全部发送完毕了，就表示发送成功
            return;
        }
        iAsduSender().chSendTotalCall101TpData(tpDataType, iter.next()).addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture future) throws Exception {
                if (!future.isSuccess()) {
                    promise.setFailure(future.cause());
                    return;
                }
                sendTpInfoObj(tpDataType, iter, promise);
            }
        });
    }

    @Override
    protected IAsdu updateStateBySend(IAsdu iAsdu) throws Exception {
        TypeTag typeTag = iAsdu.getTypeTag();
        Cot cot = iAsdu.getCot();
        Pn pn = cot.getPn();
        Cause cause = cot.getCause();
        if (typeTag == TypeTag.C_CI_NA_1) { // 计数量召唤
            if (cause == Cause.COT_ACTCON) { // 激活确认
                // 检查状态
                checkStateIn(State.R_NEW_REQ);
                if (pn == Pn.PN_YES) { // 肯定确认
                    // 肯定确认
                    setState(State.S_ACK_YES);
                } else { // 否定确认
                    // 否定确认
                    setState(State.S_ACK_NO);
                    // 恢复为结束状态
                    setState(State.FINISHED);
                }
            } else if (cause == Cause.COT_ACTTERM) { // 激活终止
                // 激活终止
                if (pn == Pn.PN_YES) {

                } else {

                }
                setState(State.FINISHED);
            }
        } else if (typeTag == TypeTag.M_IT_NA_1 || typeTag == TypeTag.M_IT_TA_1) { // 累计量
            if (cause == Cause.COT_REQCOGEN) { // 响应计数量召唤
                checkStateIn(State.S_ACK_YES, State.S_TP_TRANSMITTING); // 前提条件：肯定确认
                setState(State.S_TP_TRANSMITTING); // 表示进行遥脉传输
            }
        }

        return iAsdu;
    }

    @Nullable
    @Override
    protected ScheduledTask getScheduledTask() {
        return null;
    }
}
